/////////////////////////////////////////////////////////////////////////
//
// Amuse Engine SDK - visibility
// Copyright( c) 2015.  All Rights Reserved
//
// File:		AEVisibilityData.inl
// Author:		Gianluca Belardelli
// Date:		19/06/2015
//
/////////////////////////////////////////////////////////////////////////
#ifndef _AEVISIBILITYDATA_INL_
#define _AEVISIBILITYDATA_INL_

AE_FORCEINLINE void AEVisibilityData::SetBoundingBox( const AEBoundingBox &bbBox ) 
{ 
	m_bbBoundingBox = bbBox; 
}

AE_FORCEINLINE const AEBoundingBox &AEVisibilityData::GetBoundingBox( void ) const 
{ 
	return m_bbBoundingBox; 
}

AE_FORCEINLINE void AEVisibilityData::SetFarClipDistance( AEFLOAT32 fClipDistance )
{
	m_fFarClipDistance = fClipDistance;
}

AE_FORCEINLINE AEFLOAT32 AEVisibilityData::GetFarClipDistance( void ) const
{ 
	return m_fFarClipDistance;
}

AE_FORCEINLINE void AEVisibilityData::SetNearClipDistance( AEFLOAT32 fClipDistance )
{
	m_fNearClipDistance = fClipDistance;
}

AE_FORCEINLINE AEFLOAT32 AEVisibilityData::GetNearClipDistance( void ) const
{ 
	return m_fNearClipDistance;
}

AE_FORCEINLINE AEUINT32 AEVisibilityData::GetVisibleBitmask( void ) const 
{ 
	return m_uiVisibleMask; 
}

AE_FORCEINLINE void AEVisibilityData::SetClipMode( AEINT32 nFlags )
{
	AEASSERT_MSG(( nFlags & AEVIS_PERFORM_LODTEST )==nFlags,"Combinazione di flag invalida");
	m_nPerformTestFlags &= ( ~AEVIS_PERFORM_LODTEST );
	m_nPerformTestFlags |= nFlags;
}

AE_FORCEINLINE AEINT32 AEVisibilityData::GetClipMode( AEINT32 nMask ) const
{
	return m_nPerformTestFlags & nMask;
}

AE_FORCEINLINE AEINT32 AEVisibilityData::GetVisibilityTestFlags( void ) const
{
	return m_nPerformTestFlags;
}

AE_FORCEINLINE void AEVisibilityData::SetVisibilityTestFlags( AEINT32 nFlags )
{
	m_nPerformTestFlags = nFlags;
}

AE_FORCEINLINE void AEVisibilityData::AddVisibilityTestFlag( AEINT32 nFlags )
{
	m_nPerformTestFlags |= nFlags;
}

AE_FORCEINLINE void AEVisibilityData::RemoveVisibilityTestFlag( AEINT32 nFlags )
{
	m_nPerformTestFlags &= ~nFlags;
}

AE_FORCEINLINE const AEVector4f &AEVisibilityData::GetClipReference( void ) const
{
	return m_vcClipReference;
}

AE_FORCEINLINE AEBOOL32 AEVisibilityData::IsNearOrFarClipped( const AEVector4f &vcCameraPos, AEFLOAT32 fLODScaleSqr ) const
{
	AEINT32 nLODMode = m_nPerformTestFlags & AEVIS_PERFORM_LODTEST;
	AEFLOAT32 fDistSqr;

	switch( nLODMode )
	{
		case AELODTEST_NONE:
			return AEFALSE;
		case AELODTEST_CLIPPOSITION:
			fDistSqr = vcCameraPos.GetDistanceToSquared( m_vcClipReference );
			fDistSqr *= fLODScaleSqr;
			break;
		case AELODTEST_BOUNDINGBOX:
			fDistSqr = m_bbBoundingBox.GetDistanceToSquared( vcCameraPos ) * fLODScaleSqr;
			break;
		default:
			AEASSERT_MSG(AEFALSE,"Combinazione invalida di flag per il LOD.");
			return AEFALSE;
	}

	return IsNearOrFarClipped( fDistSqr );
}

AE_FORCEINLINE AEBOOL32 AEVisibilityData::IsNearOrFarClipped( AEFLOAT32 fDistSqr ) const
{
	return ( ( m_fNearClipDistance > 0.f ) && ( fDistSqr < ( m_fNearClipDistance*m_fNearClipDistance ) ) ) 
    	|| ( ( m_fFarClipDistance > 0.f ) && ( fDistSqr >= ( m_fFarClipDistance*m_fFarClipDistance ) ) );
}

AE_FORCEINLINE AEFLOAT32 AEVisibilityData::GetDistanceToPlane( const AEVector4f &vcCameraPos, AEFLOAT32 fLODScaleSqr, AEFLOAT32 fPlane ) const
{
	AEINT32 nLODMode = m_nPerformTestFlags & AEVIS_PERFORM_LODTEST;
	AEFLOAT32 fDistSqr;

	switch( nLODMode )
	{
		case AELODTEST_NONE:
			fDistSqr = 0.0f;
			break;
		case AELODTEST_CLIPPOSITION:
			fDistSqr = vcCameraPos.GetDistanceToSquared( m_vcClipReference );
			fDistSqr *= fLODScaleSqr;
			break;
		case AELODTEST_BOUNDINGBOX:
			fDistSqr = m_bbBoundingBox.GetDistanceToSquared( vcCameraPos ) * fLODScaleSqr;
			break;
		default:
			AEASSERT_MSG( AEFALSE, "Invalid combination of LOD flags" );
			return AEFALSE;
	}

	return AEMath::Sqrt(fDistSqr) - fPlane;
}

AE_FORCEINLINE AEBOOL32 AEVisibilityData::IsClipped( AEUINT32 uiFilterMask, const AEVector4f& vcCameraPos, AEFLOAT32 fLODScaleSqr ) const
{
	if( ( m_uiVisibleMask & uiFilterMask ) == 0 || ( ( m_nPerformTestFlags & ( AEVIS_EXCLUDED_FROM_VISTEST | AEVIS_IS_INACTIVE ) ) ) != 0 )
  		return AETRUE;
	
	return IsNearOrFarClipped( vcCameraPos, fLODScaleSqr );
}

AE_FORCEINLINE void AEVisibilityData::TranslateVisibility( const AEVector4f &vcOffset )
{
	m_vcClipReference.Add( vcOffset );
	m_bbBoundingBox.Translate( vcOffset );
}

AE_FORCEINLINE void AEVisibilityData::MergeVisibilityData( const AEVisibilityData &vdOther )
{
	m_uiVisibleMask |= vdOther.m_uiVisibleMask;

	if( vdOther.m_bbBoundingBox.IsValid() )
		m_bbBoundingBox.IncludeAabb( vdOther.m_bbBoundingBox );
	
	m_fNearClipDistance = AEMath::Min( m_fNearClipDistance, vdOther.m_fNearClipDistance );
	
	if( vdOther.m_fFarClipDistance >= 0.f && m_fFarClipDistance >= 0.f )
		m_fFarClipDistance = AEMath::Max( m_fFarClipDistance, vdOther.m_fFarClipDistance );
	else
		m_fFarClipDistance = -1.f;
}

#endif // _AEVISIBILITYDATA_INL_
